﻿module Jzc.Scanner.Models.NFAModel

open System.Collections.Generic
open System.Collections.ObjectModel
open  Jzc.Scanner.RegularExpressions

type NFAEdge=
    val Symbol:char
    val TargetState:NFAState
    new (t) = {Symbol='ε';TargetState=t}
    new (s,t)={Symbol=s;TargetState=t}

and NFAState =
    val OutEdges:List<NFAEdge>
    val Index:int
    new () = {Index=0;OutEdges=new List<NFAEdge>()}
    member t.AddEmptyEdgeTo targetState =
        t.OutEdges.Add( NFAEdge(targetState))
    member t.AddEdge  edge=
        t.OutEdges.Add(edge)
    

type NFAModel=
    [<DefaultValue>]
    val mutable EntryEdge: NFAEdge
    [<DefaultValue>]
    val mutable TailState: NFAState
    val mutable  private states:List<NFAState>
    new () =
        {states= new List<NFAState>()}
    member t.States 
        with get()=
            new  ReadOnlyCollection<NFAState>(t.states)
    member t.AddState state =
        t.states.Add(state)
    member t.AddStates states=
        for s in states do
            t.AddState(s)
    static member startIndex=0


let rec NFAConverter (expr:RegexExpr) =
    let m = new NFAModel()
    let s = new NFAState()
    match expr with
    | SymbolExpr(c) ->
        let e = new NFAEdge(c,s)
        m.EntryEdge<-e
        m.AddState(s)
        m.TailState<-s
        m
    | EmptyExpr ->
        let e = new NFAEdge(s)
        m.EntryEdge <- e
        m.AddState(s)
        m.TailState <- s
        m
    | ConcationExpr(expr1,expr2) ->
        let leftNFA= NFAConverter(expr1)
        let rightNFA= NFAConverter(expr2)
        m.AddStates(leftNFA.States)
        m.AddStates(rightNFA.States)
        leftNFA.TailState.AddEdge(rightNFA.EntryEdge)
        m.EntryEdge<-leftNFA.EntryEdge
        m.TailState<-rightNFA.TailState
        m
    | KleeneStarExpr(expr) ->
        let nfa = NFAConverter(expr)
        nfa.TailState.AddEmptyEdgeTo(s)
        s.AddEdge(nfa.EntryEdge)

        let e = new NFAEdge(s)
        m.EntryEdge <- e
        m.TailState<-s
        m.AddState(s)
        m.AddStates(nfa.States)
        m
    | AlternationExpr(expr1,expr2) ->
        let nfa1 = NFAConverter(expr1)
        let nfa2 = NFAConverter(expr2)
        let head = new NFAState()
        let tail = new NFAState()
        head.AddEdge(nfa1.EntryEdge)
        head.AddEdge(nfa2.EntryEdge)

        nfa1.TailState.AddEmptyEdgeTo(tail)
        nfa2.TailState.AddEmptyEdgeTo(tail)

        m.AddState(head)
        m.AddStates(nfa1.States)
        m.AddStates(nfa2.States)
        m.AddState(tail)

        m
